#pragma once
#include <iostream>
#include <string>
#include "Log.hpp"
#include "Thread.hpp"
#include <queue>
#include <vector>
#include "Cond.hpp"
#include "Mutex.hpp"
using namespace std;
namespace ThreadPoolModule
{
    using namespace ThreadModlue;
    using namespace LogModule;
    using namespace CondModule;  // 条件变量
    using namespace MutexModule; // 互斥锁
    static const int gunm = 5;

    template <typename T>
    class ThreadPool
    {
    private:
        void WakeUpAllThread() // 唤醒所有的线程
        {
            LockGuard lockguard(_mutex);
            // 我们所有的线程都在vector中
            if (_sleepernum > 0)                         // 如果有睡眠的线程，那么我们就唤醒它们
                _cond.Broadcast();                       // 唤醒所有在条件变量下等待的线程
            LOG(LogLevel::INFO) << "所有线程都被唤醒了"; // 打印日志信息
        }
        void WakeUpOne()
        {
            cond.Signal();                             // 唤醒一个线程
            LOG(LogLevel::INFO) << "一个线程被唤醒了"; // 打印日志信息
        }

        // 单例模式是私有的
        ThreadPool(int num = gunm)
            : _num(num), _isrunning(false), _sleepernum(0)
        {
            for (int i = 0; i < num; i++)
            {
                // 让所有的对象运行这个任务处理函数
                _threads.emplace_back([&this]()
                                      { HandlerTask(); });

                LOG(LogLevel::INFO) << "线程池创建成功"; // 打印日志信息
            }
        }
        void Start() // 让所有的线程启动
        {
            if (_isrunning)
                return;                   // 如果线程池已经启动了的话，那么我们就进行返回操作
            _isrunning = true;            // 走到这里就说明我们线程池是没有启动的，那么我们就进行启动操作
            for (auto &thread : _threads) // 遍历线程池
            {
                thread.Start();                                         // 启动线程
                //LOG(LogLevel::INFO) << "线程启动成功" << thread.Name(); // 打印日志信息
            }
        }

        // 我们是不允许有人拿单例做拷贝
        // 将拷贝构造和赋值禁用了
        ThreadPool(const ThreadPool<T> &) = delete;
        ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;

    public:
        // 前面加一个static关键字，表示这个函数是一个静态函数，在不创建对象的情况下直接进行使用操作
        // static关键字的作用是将函数的作用域限制在当前文件中，而不是整个程序中。
        static ThreadPool<T> *GetInstance() // 单例模式的获取实例函数
        {
            //通过双层判断，提高获取单例的效率
            if (inc == nullptr)//一开始我们的inc是空的，线程都进到条件判断里面了，然后进行了加锁操作，只有一个线程进入到内部的条件判断里面了
            //然后就创建了一个对象了，其他线程进行内部条件判断的时候就不符合了，因为最开始的那个线程已经创建了一个对象了
            {
                LockGuard lockguard(_lock);
                LOG(LogLevel::DEBUG) << "获取单例" << endl;
                // 第一次使用的时候，为空，那么我们就创建对象
                // 从第二次开始调用，我们就返回inc了，返回单例
                if (inc == nullptr) // 说明我们当前对象是没有被创建过的
                {
                    LOG(LogLevel::DEBUG) << "首次使用单例，创建之...." << endl;
                    inc = new ThreadPool<T>(); // 创建对象
                    // 将地址放到inc中去
                    inc->Start(); // 启动线程池
                }
            }
            return inc;
        }
        void Stop() // 让线程池停止
        {
            if (!_isrunning)
                return;         // 如果线程池已经停止了，那么我们就进行返回操作
            _isrunning = false; // 线程池停止标志

            // 线程池退出的时候，内部的任务，应该被完全取完，必须得任务都被取完了，并且任务状态是false的情况，我们才能进行退出操作

            // 如果所有的线程都是睡眠的，那么我们就得将所有的线程全部唤醒了，让它们都进行退出操作
            WakeUpAllThread(); // 唤醒所有的线程
        }
        void Join()
        {
            // if(!_isrunning)//如果线程池没有创建的话，那么我们直接退了
            //     return;
            for (auto &thread : _threads) // 遍历线程池
            {
                thread.Join(); // 等待线程结束
            }
        }
        void HandlerTask()
        {
            char name[128];
            pthread_getname_np(pthread_self(), name, sizeof(name));
            // 这行代码的作用是获取当前线程的名称，并将其存储在 name 字符数组中
            while (true)
            {
                T t;
                {
                    LockGuard lockguard(_mutex);
                    // 当你将线程唤醒，不仅仅是因为线程池为空了，也可能因为线程池要退出了
                    // 我们的线程池必须是空的，并且线程池不退出的情况，我们就进行下面的休眠等待操作
                    while (_taskq.empty()) // 如果任务队列为空，则等待条件变量通知
                    {
                        _sleepernum++;      // 睡眠线程的个数加一
                        _cond.Wait(_mutex); // 任务是空的，那么线程就得进行等待操作了
                        _sleepernum--;      // 睡眠线程的个数减一
                    }
                    // 内部的线程被唤醒
                    if (_isrunning == false && _taskq.empty()) // 如果线程池已经停止了，并且任务队列为空，那么线程就退出了
                    {
                        LOG(LogLevel::INFO) << name << "退出了，线程池退出了&&任务队列为空" break;
                    }
                    // 走到这里一定有任务的
                    t = _taskq.front();
                    _taskq.pop();
                    // 将任务从队列中取出来了
                }
                t(); // 处理任务，是不需要在临界区内进行任务处理的
            }
        }
        bool Enqueue(const T &t) // 向任务队列中添加任务
        {
            if (_isrunning) // 线程池必须得启动了才能进行入队列的操作
            {
                LockGuard lockguard(_mutex);
                _taskq.push(t); // 向任务队列中添加任务
                // 通知一个线程进行任务处理
                if (_thread.size() - _sleepernum == 0) // 如果线程池-线程所休眠的个数等于0的话就说明没有一个线程在工作的,
                    WakeUpOne();                       // 唤醒一个线程进行任务处理
                return true;
            }
            return false;
        }
        ~ThreadPool()
        {
        }

    private:
        vector<Thread> _threads; // 存储 Thread 类型的元素。
        int _num;                // 线程池中，线程的个数
        queue<T> _taskq;         // 任务队列
        Cond _cond;              // 条件变量
        Mutex _mutex;            // 互斥锁

        bool _isrunning; // 线程池是否正在运行
        int _sleepernum; // 睡眠线程的个数

        // 给外部提供一个指针,线程池的静态指针
        static ThreadPool<T> *inc; // 单例指针
        static Mutex _lock;        // 给单例加锁
    };

    template <typename T>
    ThreadPool<T> *ThreadPool<T>::inc = nullptr; // 线程池的静态指针,在类外进行初始化操作
    // 将锁进行初始化
    template <typename T>
    Mutex ThreadPool<T>::_lock; // 给单例加锁
}